package com.flesler.stickjoy.net.reader
{
	import com.flesler.stickjoy.net.token.ComponentToken;
	import com.flesler.stickjoy.net.token.ControllerToken;
	import com.flesler.stickjoy.net.token.value.AnalogComponentValue;
	import com.flesler.stickjoy.net.token.value.ComponentValue;
	import com.flesler.stickjoy.net.token.value.DigitalComponentValue;
	
	import flash.events.EventDispatcher;
	import flash.utils.IDataInput;

	// TODO: implement a pool or a map of tokens to avoid creating so many over time 
	
	/**
	 * Dispatched every time a full set of changes for a controlled is received
	 * @eventType com.flesler.stickjoy.net.reader.InputReaderEvent.INPUT
	 */
	[Event(name='input', type='com.flesler.stickjoy.net.reader.InputReaderEvent')]
	
	public final class InputReader extends EventDispatcher
	{
		private var stream:IDataInput;
		
		private var components:Array = [];
		private var controllerKey:String;
		
		public function InputReader(stream:IDataInput)
		{
			this.stream = stream;
		}
		
		public function read():void
		{
			while (stream.bytesAvailable) 
			{
				var byte:int = stream.readByte();
				// Value or controller key
				switch (byte)
				{
					case InputTokenType.TRUE	: readDigital(true) ; break;
					case InputTokenType.FALSE	: readDigital(false); break;
					case InputTokenType.NEGATIVE: readAnalog(-1)    ; break;
					case InputTokenType.CENTER	: readAnalog( 0)    ; break;
					case InputTokenType.POSITIVE: readAnalog( 1)    ; break;
					case InputTokenType.REMOVED : removed()         ; break;
					default:
						// Controller key
						// TODO: Add some error checking before assuming controllerKey
						flush();
						controllerKey = char(byte);
					break;
				}
			}
		
			flush();
		}
		
		private function readComponent(value:ComponentValue):void
		{
			var key:String = char(stream.readByte());
			components.push(new ComponentToken(key, value));
		}
		
		private function readDigital(value:Boolean):void
		{
			readComponent(new DigitalComponentValue(value));
		}
		
		private function readAnalog(value:Number):void
		{
			readComponent(new AnalogComponentValue(value));
		}
		
		private function removed():void
		{
			flush();
			notify(null);
		}
		
		private function flush():void
		{
			if (!components.length)
				return;
			
			notify(components);
			components.length = 0;
		}
		
		private function notify(components:Array):void
		{
			var token:ControllerToken = new ControllerToken(controllerKey, components);
			dispatchEvent(new InputReaderEvent(InputReaderEvent.INPUT, token));
		}
		
		private function char(byte:int):String
		{
			return String.fromCharCode(byte);
		}
		
		public function destroy():void
		{
			components = null;
			stream = null;
		}
	}
}